package com.pt25.base.dep.es;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.TransportOptions;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.pt25.base.constants.BaseConstants;
import com.pt25.base.util.CollectionUtil;
import com.pt25.base.util.StrUtil;
import jakarta.annotation.Resource;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.ssl.SSLContextBuilder;
import org.elasticsearch.client.RestClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

@Configuration
class EsConfig {
    @Resource
    private EsProperties esProperties;

    private static final Map<String, ElasticsearchClient> elasticsearchClientCacheMap = new HashMap<>();

    @Bean("elasticsearchClientMap")
    Map<String, ElasticsearchClient> elasticsearchClientMap() {
        Map<String, ElasticsearchClient> elasticsearchClientMap = new LinkedHashMap<>();
        if (CollectionUtil.isNotEmpty(esProperties.getConfigs()) && esProperties.checkConfigs()) {
            esProperties.getConfigs().forEach((esConfig) -> {
                String configStr = esConfig.toString();
                if (null != elasticsearchClientCacheMap.get(configStr)) {
                    elasticsearchClientMap.put(esConfig.getName(), elasticsearchClientCacheMap.get(configStr));
                } else {
                    try {
                        elasticsearchClientMap.put(esConfig.getName(), createSecureRestClient(esConfig));
                        elasticsearchClientCacheMap.put(configStr, elasticsearchClientMap.get(esConfig.getName()));
                    } catch (NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            if (null != elasticsearchClientMap.get(esProperties.getPrimary())) {
                elasticsearchClientMap.put(BaseConstants.DEFAULT, elasticsearchClientMap.get(esProperties.getPrimary()));
            }
        }
        return elasticsearchClientMap;
    }

    private ElasticsearchClient createSecureRestClient(EsProperties.EsConfig esConfig) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        RestClient restClient = createRestClient(esConfig);
        ElasticsearchTransport transport = createElasticsearchTransport(esConfig, restClient);
        // 创建高级的ElasticsearchClient
        return new ElasticsearchClient(transport);
    }

    private RestClient createRestClient(EsProperties.EsConfig esConfig) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
        String urls = esConfig.getUrls();
        String password = esConfig.getPassword();
        String username = esConfig.getUsername();
        HttpHost[] httpHosts = Arrays.stream(urls.split(",")).map(HttpHost::create).toArray(HttpHost[]::new);
        RestClient restClient;
        if (StrUtil.isNotBlank(password) && StrUtil.isNotBlank(username)) {
            // 1. 配置认证凭证
            final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
            credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
            // 2. 配置SSL上下文（生产环境应替换为正式证书）
            SSLContext sslContext = SSLContextBuilder.create()
                    .loadTrustMaterial((chain, authType) -> true) // 信任所有证书（仅开发环境使用）
                    .build();
            // 生产环境应使用正式证书（替换上面SSLContext部分）
//            Path trustStorePath = Paths.get("/path/to/truststore.p12");
//            char[] trustStorePassword = "password".toCharArray();
//            SSLContext sslContext = SSLContextBuilder.create()
//                    .loadTrustMaterial(trustStorePath, trustStorePassword)
//                    .build();
            // 3. 创建带认证的RestClient
            restClient = RestClient.builder(httpHosts)
                    .setHttpClientConfigCallback(httpClientBuilder -> {
                        // 连接池配置
                        httpClientBuilder.setMaxConnTotal(esConfig.getMaxConnTotal()); // 最大连接数
                        httpClientBuilder.setMaxConnPerRoute(esConfig.getMaxConnPerRoute()); // 每路由最大连接数
                        httpClientBuilder.setConnectionTimeToLive(esConfig.getConnectionTimeToLive(), TimeUnit.MINUTES); // 连接存活时间
                        return httpClientBuilder
                                .setSSLContext(sslContext)
                                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                                .setDefaultCredentialsProvider(credentialsProvider);
                    })
                    .build();

        } else {
            // 创建不带认证的RestClient
            restClient = RestClient.builder(httpHosts)
                    .setHttpClientConfigCallback(httpClientBuilder -> {
                        // 连接池配置
                        httpClientBuilder.setMaxConnTotal(esConfig.getMaxConnTotal()); // 最大连接数
                        httpClientBuilder.setMaxConnPerRoute(esConfig.getMaxConnPerRoute()); // 每路由最大连接数
                        httpClientBuilder.setConnectionTimeToLive(esConfig.getConnectionTimeToLive(), TimeUnit.MINUTES); // 连接存活时间
                        return httpClientBuilder
                                .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
                    })
                    .build();
        }
        return restClient;
    }

    private ElasticsearchTransport createElasticsearchTransport(EsProperties.EsConfig esConfig, RestClient restClient) {
        ElasticsearchTransport transport;
        //兼容es7版本
        if ("7".equals(esConfig.getVersion())) {
            transport = new RestClientTransport(
                    restClient,
                    new JacksonJsonpMapper()) {
                public TransportOptions transportOptions() {
                    return new TransportOptions() {
                        @Override
                        public Collection<Map.Entry<String, String>> headers() {
                            return List.of(
                                    new AbstractMap.SimpleEntry<>("Content-Type", "application/json"),
                                    new AbstractMap.SimpleEntry<>("Accept", "application/json")
                            );
                        }

                        @Override
                        public Map<String, String> queryParameters() {
                            return Map.of(); // 无额外参数
                        }

                        @Override
                        public Function<List<String>, Boolean> onWarnings() {
                            return warnings -> {
                                warnings.forEach(warn -> System.out.println("[WARN] " + warn));
                                return true;
                            };
                        }

                        @Override
                        public Builder toBuilder() {
                            return null;
                        }
                    };
                }
            };
        } else {
            // 使用传输客户端包装低级别的RestClient
            transport = new RestClientTransport(
                    restClient, new JacksonJsonpMapper());
        }
        return transport;
    }
}
